home *** CD-ROM | disk | FTP | other *** search
- ;;; specifier.el --- Lisp interface to specifiers
- ;; Keywords: internal
-
- ;; Copyright (C) 1995 Ben Wing
-
- ;; Author: Ben Wing <wing@netcom.com>
-
- ;; This file is part of XEmacs.
-
- ;; XEmacs is free software; you can redistribute it and/or modify it
- ;; under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation; either version 2, or (at your option)
- ;; any later version.
-
- ;; XEmacs is distributed in the hope that it will be useful, but
- ;; WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- ;; General Public License for more details.
-
- ;; You should have received a copy of the GNU General Public License
- ;; along with XEmacs; see the file COPYING. If not, write to the Free
- ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- (defun make-specifier-and-init (type spec-list &optional dont-canonicalize)
- "Create and initialize a new specifier.
-
- This is a front-end onto `make-specifier' that allows you to create a
- specifier and add specs to it at the same time. TYPE specifies the
- specifier type. SPEC-LIST supplies the specification(s) to be added
- to the specifier. Normally, almost any reasonable abbreviation of the
- full spec-list form is accepted, and is converted to the full form;
- however, if optional argument DONT-CANONICALIZE is non-nil, this
- conversion is not performed, and the SPEC-LIST must already be in full
- form. See `canonicalize-spec-list'."
- (let ((sp (make-specifier type)))
- (if (not dont-canonicalize)
- (setq spec-list (canonicalize-spec-list spec-list type)))
- (add-spec-list-to-specifier sp spec-list)
- sp))
-
- ;; God damn, do I hate dynamic scoping.
-
- (defun map-specifier (ms-specifier ms-func &optional ms-locale ms-maparg)
- "Apply MS-FUNC to the specification(s) for MS-LOCALE in MS-SPECIFIER.
-
- If MS-LOCALE is a locale, MS-FUNC will be called for that locale.
- If MS-LOCALE is a locale type, MS-FUNC will be mapped over all locales
- of that type. If MS-LOCALE is 'all or nil, MS-FUNC will be mapped
- over all locales in MS-SPECIFIER.
-
- MS-FUNC is called with four arguments: the MS-SPECIFIER, the locale
- being mapped over, the inst-list for that locale, and the
- optional MS-MAPARG. If any invocation of MS-FUNC returns non-nil,
- the mapping will stop and the returned value becomes the
- value returned from `map-specifier'. Otherwise, `map-specifier'
- returns nil."
- (let ((ms-specs (specifier-spec-list ms-specifier ms-locale))
- ms-result)
- (while (and ms-specs (not ms-result))
- (let ((ms-this-spec (car ms-specs)))
- (setq ms-result (funcall ms-func ms-specifier (car ms-this-spec)
- (cdr ms-this-spec) ms-maparg))
- (setq ms-specs (cdr ms-specs))))
- ms-result))
-
- (defun canonicalize-inst-pair (inst-pair specifier-type &optional noerror)
- "Canonicalize the given INST-PAIR.
-
- SPECIFIER-TYPE specifies the type of specifier that this SPEC-LIST
- will be used for.
-
- Canonicalizing means converting to the full form for an inst-pair, i.e.
- `(TAG-SET . INSTANTIATOR)'. A single, untagged instantiator is given
- a tag set of nil (the empty set), and a single tag is converted into
- a tag set consisting only of that tag.
-
- If NOERROR is non-nil, signal an error if the inst-pair is invalid;
- otherwise return t."
- ;; OK, the possibilities are:
- ;;
- ;; a) a single instantiator
- ;; b) a cons of a tag and an instantiator
- ;; c) a cons of a tag set and an instantiator
- (cond ((valid-instantiator-p inst-pair specifier-type)
- ;; case (a)
- (cons nil inst-pair))
-
- ((not (consp inst-pair))
- ;; not an inst-pair
- (if noerror t
- ;; this will signal an appropriate error.
- (check-valid-instantiator inst-pair specifier-type)))
-
- ((and (valid-specifier-tag-p (car inst-pair))
- (valid-instantiator-p (cdr inst-pair) specifier-type))
- ;; case (b)
- (cons (list (car inst-pair)) (cdr inst-pair)))
-
- ((and (valid-specifier-tag-set-p (car inst-pair))
- (valid-instantiator-p (cdr inst-pair) specifier-type))
- ;; case (c)
- inst-pair)
-
- (t
- (if noerror t
- (signal 'error (list "Invalid specifier tag set"
- (car inst-pair)))))))
-
- (defun canonicalize-inst-list (inst-list specifier-type &optional noerror)
- "Canonicalize the given INST-LIST (a list of inst-pairs).
-
- SPECIFIER-TYPE specifies the type of specifier that this INST-LIST
- will be used for.
-
- Canonicalizing means converting to the full form for an inst-list, i.e.
- `((TAG-SET . INSTANTIATOR) ...)'. This function accepts a single
- inst-pair or any abbrevation thereof or a list of (possibly
- abbreviated) inst-pairs. (See `canonicalize-inst-pair'.)
-
- If NOERROR is non-nil, signal an error if the inst-list is invalid;
- otherwise return t."
-
- ;; OK, the possibilities are:
- ;;
- ;; a) an inst-pair or various abbrevations thereof
- ;; b) a list of (a)
- (let ((result (canonicalize-inst-pair inst-list specifier-type t)))
- (if (not (eq result t))
- ;; case (a)
- (list result)
-
- (if (not (consp inst-list))
- ;; not an inst-list.
- (if noerror t
- ;; this will signal an appropriate error.
- (check-valid-instantiator inst-list specifier-type))
-
- ;; case (b)
- (catch 'cann-inst-list
- ;; don't use mapcar here; we need to catch the case of
- ;; an invalid list.
- (let ((rest inst-list)
- (result nil))
- (while rest
- (if (not (consp rest))
- (if noerror (throw 'cann-inst-list t)
- (signal 'error (list "Invalid list format" inst-list)))
- (let ((res2 (canonicalize-inst-pair (car rest) specifier-type
- noerror)))
- (if (eq res2 t)
- ;; at this point, we know we're noerror because
- ;; otherwise canonicalize-inst-pair would have
- ;; signalled an error.
- (throw 'cann-inst-list t)
- (setq result (cons res2 result)))))
- (setq rest (cdr rest)))
- (nreverse result)))))))
-
- (defun canonicalize-spec (spec specifier-type &optional noerror)
- "Canonicalize the given SPEC (a specification).
-
- SPECIFIER-TYPE specifies the type of specifier that this SPEC-LIST
- will be used for.
-
- Canonicalizing means converting to the full form for a spec, i.e.
- `(LOCALE (TAG-SET . INSTANTIATOR) ...)'. This function accepts a
- possibly abbreviated inst-list or a cons of a locale and a possibly
- abbreviated inst-list. (See `canonicalize-inst-list'.)
-
- If NOERROR is nil, signal an error if the specification is invalid;
- otherwise return t."
- ;; OK, the possibilities are:
- ;;
- ;; a) an inst-list or some abbrevation thereof
- ;; b) a cons of a locale and an inst-list
- (let ((result (canonicalize-inst-list spec specifier-type t)))
- (if (not (eq result t))
- ;; case (a)
- (cons 'global result)
-
- (if (not (consp spec))
- ;; not a spec.
- (if noerror t
- ;; this will signal an appropriate error.
- (check-valid-instantiator spec specifier-type))
-
- (if (not (valid-specifier-locale-p (car spec)))
- ;; invalid locale.
- (if noerror t
- (signal 'error (list "Invalid specifier locale" (car spec))))
-
- ;; case (b)
- (let ((result (canonicalize-inst-list (cdr spec) specifier-type
- noerror)))
- (if (eq result t)
- ;; at this point, we know we're noerror because
- ;; otherwise canonicalize-inst-list would have
- ;; signalled an error.
- t
- (cons (car spec) result))))))))
-
- (defun canonicalize-spec-list (spec-list specifier-type &optional noerror)
- "Canonicalize the given SPEC-LIST (a list of specifications).
-
- SPECIFIER-TYPE specifies the type of specifier that this SPEC-LIST
- will be used for.
-
- Canonicalizing means converting to the full form for a spec-list, i.e.
- `((LOCALE (TAG-SET . INSTANTIATOR) ...) ...)'. This function accepts
- a possibly abbreviated specification or a list of such things. (See
- `canonicalize-spec'.) This is the function used to convert spec-lists
- accepted by `set-specifier' and such into a form suitable for
- `add-spec-list-to-specifier'.
-
- This function tries extremely hard to resolve any ambiguities,
- and the built-in specifier types (font, image, toolbar, etc.) are
- designed so that there won't be any ambiguities.
-
- If NOERROR is nil, signal an error if the spec-list is invalid;
- otherwise return t."
- ;; OK, the possibilities are:
- ;;
- ;; a) a spec or various abbrevations thereof
- ;; b) a list of (a)
- (let ((result (canonicalize-spec spec-list specifier-type t)))
- (if (not (eq result t))
- ;; case (a)
- (list result)
-
- (if (not (consp spec-list))
- ;; not a spec-list.
- (if noerror t
- ;; this will signal an appropriate error.
- (check-valid-instantiator spec-list specifier-type))
-
- ;; case (b)
- (catch 'cann-spec-list
- ;; don't use mapcar here; we need to catch the case of
- ;; an invalid list.
- (let ((rest spec-list)
- (result nil))
- (while rest
- (if (not (consp rest))
- (if noerror (throw 'cann-spec-list t)
- (signal 'error (list "Invalid list format" spec-list)))
- (let ((res2 (canonicalize-spec (car rest) specifier-type
- noerror)))
- (if (eq res2 t)
- ;; at this point, we know we're noerror because
- ;; otherwise canonicalize-spec would have
- ;; signalled an error.
- (throw 'cann-spec-list t)
- (setq result (cons res2 result)))))
- (setq rest (cdr rest)))
- (nreverse result)))))))
-
- (defun set-specifier (specifier value &optional how-to-add)
- "Add some specifications to SPECIFIER.
- VALUE can be a single instantiator or tagged instantiator (added as a
- global specification), a list of tagged and/or untagged instantiators
- (added as a global specification), a cons of a locale and instantiator
- or locale and instantiator list, a list of such conses, or nearly any
- other reasonable form. More specifically, VALUE can be anything
- accepted by `canonicalize-spec-list'.
-
- HOW-TO-ADD is the same as in `add-spec-to-specifier'.
-
- Note that `set-specifier' is exactly complementary to `specifier-specs'
- except in the case where SPECIFIER has no specs at all in it but nil
- is a valid instantiator (in that case, `specifier-specs' will return
- nil (meaning no specs) and `set-specifier' will interpret the `nil'
- as meaning \"I'm adding a global instantiator and its value is `nil'\"),
- or in strange cases where there is an ambiguity between a spec-list\n\
- and an inst-list, etc. (The built-in specifier types are designed\n\
- in such a way as to avoid any such ambiguities.)
-
- If you want to work with spec-lists, you should probably not use these
- functions, but should use the lower-level functions `specifier-spec-list'
- and `add-spec-list-to-specifier'. These functions always work with
- fully-qualified spec-lists; thus, there is no possibility for
- ambiguity and no need to go through the function `canonicalize-spec-list',
- which is potentially time-consuming."
- (add-spec-list-to-specifier
- specifier
- (canonicalize-spec-list value (specifier-type specifier))
- how-to-add))
-